<?php

/*
 * This file is part of PHP ENTITY LINK.
 *
 *  PHP ENTITY LINK is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  PHP ENTITY LINK is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with PHP ENTITY LINK.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Copyright (C) 2010 Peter Jacques, Michael Kyndt
 *
*/

namespace php_entity_link{

    use \Exception;

    require_once ("Hostfile.class.php");    

    /**
     * This class is responsible for connection to the database and executing
     * query statements on the database.
     *
     * @author Peter Jacques, Michael Kyndt
     * @package php entity link
     * @version 1.0.0
     */
    class DS {

        /**
         * Array contains one unique DS class per connection in the hostfile
         * @static
         * @var <array>
         */
        private static $arr_unique_conns = array();

        /**
         * Placeholder for the used connection.
         * @var <MySQL link identifier>
         */
        private $connection;
        /**
         * Placeholders tells to use mysql_pconnect or mysql_connect
         * @var <type>
         */
        private $persistent;

        /**
         * the simple php types where the db types are mapped to
         */
        const DB_TYPE_STRING = 'text';
        const DB_TYPE_TEXT = 'text';
        const DB_TYPE_LONG = 'long';
        const DB_TYPE_INT = 'int';
        const DB_TYPE_DOUBLE = 'double';
        const DB_TYPE_DATE = 'date';
        const DB_TYPE_ENUM = 'enum';
        const DB_TYPE_FLOAT = 'float';

        /**
         * Contains the connection settings for the instance of the DS class
         * @var <type>
         */
        private $conn_settings;

        /**
         * Private constructor for the creating of an instance
         * @param <string> $connection_name connection name defined in the hostfile
         */
        private function __construct($connection_name,$db=null) {
            $this->conn_settings = Hostfile::getConnection_params($connection_name);

            if(isset($db)){
                $this->conn_settings[Hostfile::DB] = $db;
            }

            $this->persistent = $this->conn_settings[Hostfile::PERSISTENT];
            $this->connection = null;
        }//__construct

        /**
         * Call this function to require an instance from the ds class for a specific
         * connection declared in the hostfile
         * @static
         * @param <string> $connection_name connection name declared in the hostfile
         * @return <DS> an instance form the ds class
         */
        public static function getInstance($connection_name,$db=null) {
            if(!isset(self::$arr_unique_conns[$connection_name.$db])) {
                self::$arr_unique_conns[$connection_name.$db] = new DS($connection_name,$db);
            }
            return self::$arr_unique_conns[$connection_name.$db];
        }//getInstance

        /**
         * This function makes a connection to the database.
         * Throws an exception on failure.
         * @return <MySQL persistent link identifier>
         */
        private function connect() {
            $do_connect = false;
            if($this->connection == null){
                $do_connect = true;
            }
            try {
                if(!$do_connect){
                    if(@mysql_ping($this->connection)){
                        return $this->connection;
                    }else{
                        $do_connect = true;
                        @mysql_close($this->connection);
                        $this->connection = null;
                    }
                }
                if($do_connect){
                    if($this->persistent){
                        $conn = mysql_pconnect($this->conn_settings[Hostfile::HOST], $this->conn_settings[Hostfile::USER], $this->conn_settings[Hostfile::PWD]);
                    }else{
                        $conn = mysql_connect($this->conn_settings[Hostfile::HOST], $this->conn_settings[Hostfile::USER], $this->conn_settings[Hostfile::PWD]);
                    }
                    if(!$conn) {
                        throw new Exception(mysql_error());
                    }
                    try {
                        if(!mysql_select_db($this->conn_settings[Hostfile::DB], $conn)) {
                            throw new Exception(mysql_error($conn));
                        }
                        if(isset($this->conn_settings[Hostfile::NAMES])) {
                            if(!mysql_query('SET NAMES '.$this->conn_settings[Hostfile::NAMES],$conn)){
                                throw new Exception(mysql_error($link_identifier));
                            }
                        }
                        if(isset($this->conn_settings[Hostfile::COL_CONN])) {
                            if(!mysql_query('SET COLLATION_CONNECTION='.$this->conn_settings[Hostfile::COL_CONN],$conn)){
                                throw new Exception(mysql_error($conn));
                            }
                        }
                        $this->connection = $conn;
                        return $conn;
                    }catch(Exception $ex) {
                        mysql_close($conn);
                        $this->connection = null;
                        throw $ex;
                    }
                }
            }catch(Exception $ex) {
                throw $ex;
            }
        }//connect

        /**
         * Closses the connection if not presistent
         */
        private function disconnect(){
            if(!$this->persistent){
                @mysql_close($this->connection);
                $this->connection = null;
            }
        }//disconnect

        /**
         * Use this function to execute an select query.
         * Throws an exception on failure.
         * @param <string> $query the select query to be executed
         * @return <array> 2 dimensional array with key value pairs
         */
        public function select($query) {
            try {
                $conn = $this->connect();
                try {
                    $result_set = mysql_query($query, $conn);
                    if(!$result_set) {
                        throw new Exception(mysql_error($conn));
                    }
                    $rows = array();
                    while($row = mysql_fetch_assoc($result_set)) {
                        array_push($rows, $row);
                    }//next row
                    $this->disconnect();
                    return $rows;
                }catch(Exception $ex) {
                    $this->disconnect();
                    throw $ex;
                }
            }catch(Exception $ex) {
                throw $ex;
            }
        }//select

        /**
         * Use this function to execute an insert statement
         * Throws an exception on failure.
         * @param <string> $query the query to be executed
         * @return <type> return the mysql instert id
         */
        public function insert($query) {
            try {
                $conn = $this->connect();
                try {
                    if(!mysql_query($query,$conn)) {
                        throw new Exception(mysql_error($conn));
                    }
                    $id = mysql_insert_id($conn);
                    return $id;
                    $this->disconnect();
                }catch(Exception $ex) {
                    $this->disconnect();
                    throw $ex;
                }
            }catch(Exception $ex) {
                throw $ex;
            }
        }//insert

        /**
         * Use this function to execute an update query
         * Throws an exception on failure.
         * @param <string> $query the query to be executed
         */
        public function update($query) {
            try {
                $conn = $this->connect();
                try {
                    if(!mysql_query($query,$conn)) {
                        throw new Exception(mysql_error($conn));
                    }
                    $this->disconnect();
                }catch(Exception $ex) {
                    $this->disconnect();
                    throw $ex;
                }
            }catch(Exception $ex) {
                throw $ex;
            }
        }//update

        /**
         * Use this function to execute an delete query
         * Throws an exception on failure.
         * @param <string> $query the query to be executed
         */
        public function delete($query) {
            try {
                $conn = $this->connect();
                try {
                    if(!mysql_query($query,$conn)) {
                        throw new Exception(mysql_error($conn));
                    }
                    $this->disconnect();
                }catch(Exception $ex) {
                    $this->disconnect();
                    throw $ex;
                }
            }catch(Exception $ex) {
                throw $ex;
            }
        }//delete

        /**
         * Use this function to get an array form all the fields in a table
         * Throws an exception on failure.
         * @param <string> $table the table name
         * @return <array> 2 dimensional array with key value pairs
         */
        public function getFields($table) {
            $query = "SHOW FULL COLUMNS FROM ".$table." from ".$this->conn_settings[Hostfile::DB];
            try {
                $columns = $this->select($query);
                $modified_columns = array();
                for($i = 0; $i < count($columns); $i++) {
                    $column = $columns[$i];
                    $column['m_type'] = $this->map_db_type($column['Type']);
                    $modified_columns[$column['Field']] = $column;
                }//next i
                unset($columns);
                return $modified_columns;
            }catch(Exception $ex) {
                throw $ex;
            }
        }//getFieldTypes

        /**
         * Gets all the fk fields from a table
         * @param <string> $table
         * @return <array>
         */
        public function getFkFields($table){
            $query = "select * from information_schema.key_column_usage kcu
                      where kcu.REFERENCED_TABLE_NAME = '{$table}'
                      and kcu.referenced_table_name is not null
                      and kcu.CONSTRAINT_SCHEMA = '{$this->conn_settings[Hostfile::DB]}'";
            try {
                $rows = $this->select($query);
                echo $table.'<br />';
                dump($rows);
                return $rows;
            } catch (Exception $ex) {
                throw $ex;
            }
        }//getFkFields

        /**
         * Maps the db type to simple php types
         * @param <string> $type the field db type
         * @return <string> the simple php type
         */
        private function map_db_type($type) {
            if(strstr($type, "int")) {
                return self::DB_TYPE_INT;
            }
            if(strstr($type,"double")) {
                return self::DB_TYPE_DOUBLE;
            }
            if(strstr($type,"float") || strstr($type,"decimal")) {
                return self::DB_TYPE_FLOAT;
            }
            if($type == "date") {
                return self::DB_TYPE_DATE;
            }
            if(strstr($type,"enum")) {
                return self::DB_TYPE_ENUM;
            }
            return self::DB_TYPE_TEXT;
        }//map_db_type

        /**
         * Use this function to escape an sql parameter.
         * Throws an exception on failure.
         * @param <type> $theValue the value to escape
         * @param <string> $theType the simple php type. DS::DB_TYPE_STRING, ...
         * @return <type> the escapes value
         */
        public function getSQLValueString($theValue, $theType) {
            try {
                $conn = $this->connect();
                try {
                    $theValue = mysql_real_escape_string($theValue,$conn);
                    switch ($theType) {
                        case self::DB_TYPE_TEXT:
                        case self::DB_TYPE_ENUM:
                            $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
                            break;
                        case self::DB_TYPE_LONG:
                        case self::DB_TYPE_INT:
                            $theValue = ($theValue != "") ? intval($theValue) : "NULL";
                            break;
                        case self::DB_TYPE_DOUBLE:
                            $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
                            break;
                        case self::DB_TYPE_FLOAT:
                            $theValue = ($theValue != "") ? "'" . floatval($theValue) . "'" : "NULL";
                        case self::DB_TYPE_DATE:
                            $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
                            break;
                        default:
                            return $this->getSQLValueString($theValue, self::DB_TYPE_TEXT);
                            break;
                    }
                    $this->disconnect();
                    return $theValue;
                }catch(Exception $ex) {
                    $this->disconnect();
                    throw $ex;
                }
            }catch(Exception $ex) {
                throw $ex;
            }
        }//getSQLValueString

        public function getConnSetting($setting){
            return $this->conn_settings[$setting];
        }

    }//DS

}//php_entity_link

?>